/**
 * @author WY
 * @since 2024/03
 */

import { ColorSchema, RENDERORDER } from '@amcax/base';

import { THREE } from '@amcax/web-render';
const { Box3, Color, Mesh, MeshPhongMaterial } = THREE;
type Attrs = {
  geometry: THREE.BufferGeometry;
  material: THREE.MeshPhongMaterial | THREE.MeshBasicMaterial;
  modelDxid?: string; // 对应后端dxid
  visibility?: boolean;
  opacity?: number;
  renderOrder?: number;
  groups?: Array<string>; // 分组
};

export class CSMesh extends Mesh<
  THREE.BufferGeometry,
  THREE.MeshPhongMaterial | THREE.MeshBasicMaterial
> {
  modelDxid: string;
  name = 'csMesh';
  visibility?: boolean;
  opacity?: number;
  groups: Array<string>;
  _planeBox: THREE.Box3;
  parentScene: THREE.Object3D;
  private _bbox;
  constructor(attrs: Attrs) {
    super(attrs.geometry, attrs.material);
    this.geometry = attrs.geometry;
    if (attrs.geometry.groups && attrs.geometry.groups.length > 0) {
      this.geometry.groups = attrs.geometry.groups;
    }
    this.modelDxid = attrs.modelDxid;
    this.visible = attrs.visibility !== false ? true : false;
    this.opacity = attrs.opacity || 1.0;
    this.renderOrder = attrs.renderOrder || RENDERORDER.FACE;
    this.groups = attrs.groups || [];
    this.setOpacity(attrs.opacity);
  }
  get bbox(): THREE.Box3 {
    if (!this._bbox) {
      this._bbox = new Box3().expandByObject(this);
    }
    return this._bbox;
  }

  get Geometry(): THREE.BufferGeometry {
    return (this.geometry as THREE.BufferGeometry).clone();
  }
  setEmissive(color: number) {
    if (this.material instanceof MeshPhongMaterial) {
      this.material.emissive.setHex(color);
      if (this.children.length > 0) {
        this.children.forEach((mesh) => {
          (mesh as CSMesh).setEmissive(ColorSchema.emissive.map);
        });
      }
    }
  }
  recoverEmissive() {
    if (this.material instanceof MeshPhongMaterial) {
      this.material.emissive.setHex(ColorSchema.emissive.default);
      if (this.children.length > 0) {
        this.children.forEach((mesh) => {
          (mesh as CSMesh).recoverEmissive();
        });
      }
    }
  }
  setGroups(groups: string) {
    this.groups.unshift(groups);
  }
  setRenderOrder(r: number) {
    this.renderOrder = r;
  }
  setColor(color: number | Array<number>) {
    if (color instanceof Array) {
      this.material.color = new Color().fromArray(color);
    } else {
      this.material.color = new Color(color);
    }
  }
  getBoundBox() {
    const box = new Box3().expandByObject(this);
    return box;
  }
  selected(color: number = ColorSchema.select.shell) {
    this.material.color = new Color(color);
    this.material.opacity = 0.5;
    this.renderOrder = RENDERORDER.FACE_S;
  }
  transferByMatrix4(matrix: THREE.Matrix4) {
    this.geometry.applyMatrix4(matrix);
    if (this.children.length > 0) {
      this.children.forEach((i) => {
        (i as CSMesh).transferByMatrix4(matrix);
      });
    }
  }
  destroy() {
    this.parentScene && this.parentScene.remove(this);
    this.geometry.dispose();
  }
  addTo(scene: THREE.Object3D) {
    this.parentScene = scene;
    this.parentScene.add(this);
  }
  addChildMesh(mesh: CSMesh) {
    const modelDxid = mesh.modelDxid;
    this.children.forEach((m) => {
      if ((m as CSMesh).modelDxid === modelDxid) {
        this.remove(m);
      }
    });
    this.add(mesh);
  }
  cloneSelf() {
    return new CSMesh({
      geometry: this.geometry.clone(),
      material: this.material.clone(),
      visibility: this.visible,
      renderOrder: this.renderOrder,
      groups: this.groups,
    });
  }
  getAttributes() {
    return {
      geometry: this.geometry.clone(),
      material: this.material.clone(),
      renderOrder: this.renderOrder,
    };
  }
  setOpacity(opacity: number) {
    if (!opacity) return;
    this.material.opacity = opacity;
    if (opacity < 1.0) {
      this.material.transparent = true;
      this.material.depthWrite = false;
    } else {
      this.material.transparent = false;
      this.material.depthWrite = true;
    }
  }
  resetOpacity() {
    this.material.opacity = this.opacity;
    if (this.opacity < 1.0) {
      this.material.transparent = true;
      this.material.depthWrite = false;
    } else {
      this.material.transparent = false;
      this.material.depthWrite = true;
    }
  }
}
